/***************************************************************************
 * Copyright 2012-2013 TXT e-solutions SpA
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the contract ICT-257367.
 *
 * Authors:
 *      Salvatore Piccione (TXT e-solutions SpA)
 *
 * Contributors:
 *        Domenico Rotondi (TXT e-solutions SpA)
 **************************************************************************/
package it.txt.ens.client.impl;

import it.txt.ens.client.core.ENSBrokerConnectionParameters;
import it.txt.ens.client.core.factory.ENSBrokerConnectionParametersFactory;
import it.txt.ens.core.util.validator.BrokerPortValidator;

import java.util.Date;

/**
 * Implements the interface {@link ENSBrokerConnectionParameters}.
 * 
 * @author Salvatore Piccione (TXT e-solutions SpA - salvatore.piccione AT txtgroup.com)
 * @author Domenico Rotondi (TXT e-solutions SpA - domenico.rotondi AT txtgroup.com)
 *
 */
public class BasicENSBrokerConnectionParameters implements ENSBrokerConnectionParameters {
    
    private final String subjectID;
    private final String accessToken;
    private final String brokerHost;
    private final int brokerPort;
    private final String destinationName;
    private final Date sessionExpiration;
    private final String sessionToken;
    private final String virtualHost;
    private final boolean tlsEnabled;
    private final int mgmtPort;
    
    private BasicENSBrokerConnectionParameters (Builder builder) {
        subjectID = builder.subjectID;
        accessToken = builder.accessToken;
        brokerHost = builder.brokerHost;
        brokerPort = builder.brokerPort;
        destinationName = builder.destinationName;
        sessionExpiration = builder.sessionExpiration;
        sessionToken = builder.sessionToken;
        virtualHost = builder.virtualHost;
        tlsEnabled = builder.tlsEnabled;
        mgmtPort = builder.mgmtPort;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#getSubjectID()
     */
    public String getSubjectID() {
        return subjectID;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#getAccessToken()
     */
    public String getAccessToken() {
        return accessToken;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#getBrokerHost()
     */
    public String getBrokerHost() {
        return brokerHost;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#getBrokerPort()
     */
    public int getBrokerPort() {
        return brokerPort;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#getVirtualHost()
     */
    public String getVirtualHost() {
        return virtualHost;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#getDestinationName()
     */
    public String getDestinationName() {
        return destinationName;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#getSessionToken()
     */
    public String getSessionToken() {
        return sessionToken;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#getSessionExpiration()
     */
    public Date getSessionExpiration() {
        return sessionExpiration;
    }

    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSConnectionParameters#isTLSEnabled()
     */
    public boolean isTLSEnabled() {
        return tlsEnabled;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder("{subjectID:\"");
        builder.append(subjectID);
        builder.append("\", accessToken:\"");
        builder.append(accessToken);
        builder.append("\", brokerHost:\"");
        builder.append(brokerHost);
        builder.append("\", brokerPort:\"");
        builder.append(brokerPort);
        builder.append("\", brokerVirtualHost:\"");
        builder.append(virtualHost);
        builder.append("\", destination:\"");
        builder.append(destinationName);
        builder.append("\", isTLSEnabled:\"");
        builder.append(tlsEnabled);
        builder.append("\", sessionToken:\"");
        builder.append(sessionToken);
        builder.append("\", sessionExpiration:\"");
        builder.append(new java.text.SimpleDateFormat(DATE_TIME_FORMAT).format(sessionExpiration));
        builder.append("\"}");
        return builder.toString();
    }

    /**
     * Creates instances of {@link BasicENSBrokerConnectionParametersFactory} using the <i>Builder</i> pattern.
     * 
     * @author Salvatore Piccione (TXT e-solutions SpA - salvatore.piccione AT txtgroup.com)
     * @author Domenico Rotondi (TXT e-solutions SpA - domenico.rotondi AT txtgroup.com)
     */
    public static class Builder {
                
        private final String subjectID;
        private final String accessToken;
        private final String destinationName;
        private final String sessionToken;
        private String brokerHost = ENSBrokerConnectionParametersFactory.DEFAULT_BROKER_HOST;
        private int brokerPort = ENSBrokerConnectionParametersFactory.DEFAULT_BROKER_PORT;
        private Date sessionExpiration = ENSBrokerConnectionParametersFactory.DEFAULT_SESSION_EXPIRATION;
        private String virtualHost = ENSBrokerConnectionParametersFactory.DEFAULT_VIRTUAL_HOST;
        private boolean tlsEnabled = ENSBrokerConnectionParametersFactory.DEFAULT_TLS_FLAG;
        private int mgmtPort = ENSBrokerConnectionParametersFactory.DEFAULT_MGMT_PORT;
        
        /**
         * Creates a new Builder whose mandatory fields are set with the given parameters.
         * 
         * @param subjectID the identifier of the ENS broker user.
         * @param accessToken the access token to be used when connecting to the ENS broker.
         * @param destinationName the name of the virtual host resource
         * to be used by the client (i.e. either an AMQP queue or exchange).
         * @param sessionToken the session token.
         * @throws IllegalArgumentException if at least one of the input parameters is <code>null</code>
         * or an empty string.
         */
        public Builder (String subjectID, String accessToken, String destinationName, String sessionToken)
        throws IllegalArgumentException {
            if (subjectID == null)
                throw new IllegalArgumentException("The subjectID cannot be null");
            if (subjectID.isEmpty())
                throw new IllegalArgumentException("The subjectID cannot be an empty string");
            if (accessToken == null)
                throw new IllegalArgumentException("The access token cannot be null");
            if (accessToken.isEmpty())
                throw new IllegalArgumentException("The access token cannot be an empty string");
            if (destinationName == null)
                throw new IllegalArgumentException("The destination name cannot be null");
            if (destinationName.isEmpty())
                throw new IllegalArgumentException("The destination name cannot be an empty string");
            if (sessionToken == null)
                throw new IllegalArgumentException("The session token cannot be null");
            if (sessionToken.isEmpty())
                throw new IllegalArgumentException("The session token cannot be an empty string");
            this.subjectID = subjectID;
            this.accessToken = accessToken;
            this.destinationName = destinationName;
            this.sessionToken = sessionToken;
        }
        
        /**
         * Sets the host name/IP address of the machine the ENS broker is deployed on.
         * @param brokerHost the host name/IP address of the machine the ENS broker is deployed on.
         * @return a reference to this builder
         * @throws IllegalArgumentException if <code>brokerHost</code> is <code>null</code>
         * or an empty string
         */
        public Builder brokerHost (String brokerHost) throws IllegalArgumentException {
            if (brokerHost == null)
                throw new IllegalArgumentException("The broker host cannot be null");
            if (brokerHost.isEmpty())
                throw new IllegalArgumentException("The broker host cannot be an empty string");
            this.brokerHost = brokerHost;
            return this;
        }
        
        /**
         * Sets the TCP listening port of the ENS broker.
         * 
         * @param brokerPort the TCP listening port of the ENS broker
         * @return a reference to this builder
         * @throws IllegalArgumentException if <code>brokerPort</code> is not in the range
         * [{@link BrokerPortValidator#MIN_PORT_NUMBER}, 
         * {@link BrokerPortValidator#MAX_PORT_NUMBER}]
         * 
         */
        public Builder brokerPort (int brokerPort) throws IllegalArgumentException {
            if (!BrokerPortValidator.isValid(brokerPort))
                throw new IllegalArgumentException("The broker host cannot be null");
            this.brokerPort = brokerPort;
            return this;
        }
        
        /**
         * Sets the HTTP listening port of the ENS Broker.
         * @param mgmtPort the HTTP listening port of the ENS Broker.
         * @return a reference to this object
         * @throws IllegalArgumentException if <code>mgmtPort</code> is not in the range
         * [{@value it.txt.ens.core.util.validator.BrokerPortValidator#MIN_PORT_NUMBER}, 
         * {@value it.txt.ens.core.util.validator.BrokerPortValidator#MAX_PORT_NUMBER}]
         */
        public Builder mgmtPort (int mgmtPort) throws IllegalArgumentException {
            if (!BrokerPortValidator.isValid(mgmtPort))
                throw new IllegalArgumentException("The broker host cannot be null");
            this.mgmtPort  = mgmtPort;
            return this;
        }
        
        /**
         * Sets the session expiration time.
         * 
         * @param sessionExpiration the instant when the ENS session expires.
         * @return a reference to this builder
         * @throws IllegalArgumentException if <code>sessionExpiration</code> is <code>null</code>
         */
        public Builder sessionExpiration (Date sessionExpiration) throws IllegalArgumentException {
            this.sessionExpiration = sessionExpiration;
            return this;
        }
        
        /**
         * Sets the flag indicating whether the connection is TLS-secured or not.
         * 
         * @param isEnabled <code>true</code> if the communication should flow through a TLS-secured channel,
         * <code>false</code> otherwise
         * @return a reference to this builder
         */
        public Builder tlsEnabled (boolean isEnabled) {
            this.tlsEnabled = isEnabled;
            return this;
        }
        
        /**
         * Sets the name of the virtual host.
         * 
         * @param virtualHost the name of the virtual host.
         * @return a reference to this builder
         * @throws IllegalArgumentException if <code>virtualHost</code> is <code>null</code> or an
         * empty string
         */
        public Builder virtualHost (String virtualHost) throws IllegalArgumentException {
            if (virtualHost == null)
                throw new IllegalArgumentException("The virtual host cannot be null");
            if (virtualHost.isEmpty())
                throw new IllegalArgumentException("The virtual host cannot be an empty string");
            this.virtualHost = virtualHost;
            return this;
        }
        
        /**
         * Creates a new <code>ENSBrokerConnectionParameters</code> object.
         * @return a new <code>ENSBrokerConnectionParameters</code> object.
         */
        public BasicENSBrokerConnectionParameters build () {
            return new BasicENSBrokerConnectionParameters(this);
        }
    }
    
    
    /**
     * Provides the default implementation of the interface {@link BasicENSBrokerConnectionParameters}.<br/>It
     * builds <code>BasicENSBrokerConnectionParameters</code> objects.
     * 
     * @author Salvatore Piccione (TXT e-solutions SpA - salvatore.piccione AT txtgroup.com)
     * @author Domenico Rotondi (TXT e-solutions SpA - domenico.rotondi AT txtgroup.com)
     *
     */
    public static class BasicENSBrokerConnectionParametersFactory implements ENSBrokerConnectionParametersFactory {
    	/**
    	 * Unique identifier of this {@code ENSBrokerConnectionParametersFactory} implementation.
    	 * 
    	 * @see it.txt.ens.client.core.osgi.RegisteredServices#ENS_BROKER_CONN_PARAMS_FACTORY 
    	 */
        public static final String IMPLEMENTATION_ID = "default";
        
        @Override
        public ENSBrokerConnectionParameters create(String subjectID,
                String accessToken, String destinationName, String sessionToken)
                throws IllegalArgumentException {
            Builder builder = new Builder(subjectID, accessToken, destinationName, sessionToken);
            return builder.build();
        }

        @Override
        public ENSBrokerConnectionParameters create(String subjectID, String accessToken, String brokerHost,
                int brokerPort, String virtualHost, String destinationName, boolean tlsEnabled,
                Date sessionExpiration, String sessionToken)
                throws IllegalArgumentException {
            Builder builder = new Builder(subjectID, accessToken, destinationName, sessionToken);
            if (null != brokerHost)
                builder.brokerHost(brokerHost);
            builder.brokerPort(brokerPort);
            if (null != virtualHost)
                builder.virtualHost(virtualHost);
            builder.tlsEnabled(tlsEnabled);
            builder.sessionExpiration(sessionExpiration);
            return builder.build();
        }

        /* (non-Javadoc)
         * @see it.txt.ens.client.core.factory.ENSBrokerConnectionParametersFactory#create(java.lang.String, java.lang.String, java.lang.String, int, int, java.lang.String, java.lang.String, boolean, java.util.Date, java.lang.String)
         */
        @Override
        public ENSBrokerConnectionParameters create(String subjectID,
                String accessToken, String brokerHost, int brokerPort,
                int mgmtPort, String virtualHost, String destinationName,
                boolean tlsEnabled, Date sessionExpiration, String sessionToken)
                throws IllegalArgumentException {
            Builder builder = new Builder(subjectID, accessToken, destinationName, sessionToken);
            if (null != brokerHost)
                builder.brokerHost(brokerHost);
            builder.brokerPort(brokerPort);
            builder.mgmtPort(mgmtPort);
            if (null != virtualHost)
                builder.virtualHost(virtualHost);
            builder.tlsEnabled(tlsEnabled);
            builder.sessionExpiration(sessionExpiration);
            return builder.build();
        }
    }


    /* (non-Javadoc)
     * @see it.txt.ens.core.ENSAuthzServiceConnectionParameters#getManagementPort()
     */
    @Override
    public int getManagementPort() {
        return this.mgmtPort;
    }
}
